package qc.module.qms.service.pointdata;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import qc.common.core.enums.qms.QmsDataFlowOperateTypeEnum;
import qc.common.core.enums.qms.QmsPointDataQualityEnum;
import qc.common.core.enums.qms.QmsPointTimeSegmentEnum;
import qc.common.core.enums.qms.QmsRawDataCalcFlagEnum;
import qc.common.core.utils.IdentifierUtil;
import qc.common.core.utils.LocalDateTimeUtil;
import qc.common.core.utils.QmsTimeUtil;
import qc.module.platform.dto.db.DBConnectionDto;
import qc.module.qms.algorithm.NumericCalc.dto.*;
import qc.module.qms.algorithm.NumericCalc.util.QmsNumericCalcUtil;
import qc.module.qms.dto.calc.QmsPointDataCalcResultDto;
import qc.module.qms.dto.calc.QmsPointDataCalcSaveResultDto;
import qc.module.qms.dto.pointdata.QmsPointDataManageResultDto;
import qc.module.qms.dto.pointdata.QmsPointNumericSaveDataDto;
import qc.module.qms.dto.rawdata.QmsRawDataDto;
import qc.module.qms.entity.QmsPoint;
import qc.module.qms.entity.QmsStationMaintenance;
import qc.module.qms.mapper.QmsCalcResultMapper;
import qc.module.qms.mapper.QmsPointMapper;
import qc.module.qms.service.QmsStationMaintenanceService;
import qc.module.qms.service.calc.QmsNumericCalcInputGetService;
import qc.module.qms.service.producer.QmsDataFlowLogProducer;
import qc.module.qms.service.rawdata.QmsRawDataRecordService;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * QMS点号数据计算Service
 * 对指定的点号时间段进行数值计算，计算结果自动保存
 * 提供给原始数据计算、点号数据触发命中点号计算等MQ消费者、定时任务定时进行点号数据计算使用
 *
 * @author QuCheng Tech
 * @since 2025/10/5
 */
@Service
public class QmsPointDataCalcService {
    private static final Logger logger = LoggerFactory.getLogger(QmsPointDataCalcService.class);

    @Autowired
    private QmsNumericCalcInputGetService numericCalcInputGetService;

    @Autowired
    private QmsDataFlowLogProducer dataFlowLogProducer;

    @Autowired
    private QmsPointDataManageService pointDataManageService;

    @Autowired
    private QmsRawDataRecordService rawDataService;

    @Autowired
    private QmsStationMaintenanceService stationMaintenanceService;

    /**
     * 计算点号数据，根据传入的参数自动完成数值计算和数据保存
     *
     * @param point      点号Entity
     * @param tmseg      点号时段类型
     * @param useMethod  使用的计算方法
     * @param beginTime  要计算的起始时间
     * @param endTime    要计算的截止时间
     * @param optype     操作类型，指示调用的来源：原始数据触发计算、点号数据触发计算、定时计算等；通过设置传入optype为UN_DEFINE控制只进行点号数据的计算
     * @param isManual   是否为人工操作
     * @param flowid     流程全局ID
     * @param fromNodeId 流程来源节点ID
     * @return qc.module.qms.dto.calc.QmsPointDataCalcSaveResultDto
     * @author QuCheng Tech
     * @since 2025/10/8
     */
    public QmsPointDataCalcSaveResultDto calcPointDatas(QmsPoint point, QmsPointTimeSegmentEnum tmseg, QmsNumericCalcUseMethod useMethod,
                                                        LocalDateTime beginTime, LocalDateTime endTime, QmsDataFlowOperateTypeEnum optype,
                                                        boolean isManual, String flowid, String fromNodeId) {
        return calc(point, tmseg, useMethod, beginTime, endTime, optype, isManual, flowid, fromNodeId, null, null, null, null, null, null);
    }

    /**
     * 计算原始数据，根据传入的参数自动完成数值计算和数据保存
     *
     * @param point                点号Entity
     * @param tmseg                点号时段类型
     * @param useMethod            使用的计算方法
     * @param beginTime            要计算的起始时间
     * @param endTime              要计算的截止时间
     * @param optype               操作类型，指示调用的来源：原始数据触发计算、点号数据触发计算、定时计算等；通过设置传入optype为UN_DEFINE控制只进行点号数据的计算
     * @param isManual             是否为人工操作
     * @param flowid               流程全局ID
     * @param fromNodeId           流程来源节点ID
     * @param regularTimeRawDatas  规则时间后的原始数据，时间和数值集合
     * @param tableCode            原始数据表编码
     * @param rawStcd              原始数据站点编码
     * @param rawColumnCode        原始数据列名
     * @param filterByStcdRawDatas 原始数据集合
     * @param connection           原始数据表所属的数据库连接信息
     * @return qc.module.qms.dto.calc.QmsPointDataCalcSaveResultDto
     * @author QuCheng Tech
     * @since 2025/10/8
     */
    public QmsPointDataCalcSaveResultDto calcRawDatas(QmsPoint point, QmsPointTimeSegmentEnum tmseg, QmsNumericCalcUseMethod useMethod,
                                                      LocalDateTime beginTime, LocalDateTime endTime, QmsDataFlowOperateTypeEnum optype,
                                                      boolean isManual, String flowid, String fromNodeId, Map<LocalDateTime, Double> regularTimeRawDatas,
                                                      String tableCode, String rawStcd, String rawColumnCode, List<QmsRawDataDto> filterByStcdRawDatas,
                                                      DBConnectionDto connection) {
        return calc(point, tmseg, useMethod, beginTime, endTime, optype, isManual, flowid, fromNodeId, regularTimeRawDatas,
                tableCode, rawStcd, rawColumnCode, filterByStcdRawDatas, connection);
    }

    /**
     * 计算指定点号时间段内的数据，根据传入的参数自动完成数值计算和数据保存
     *
     * @param point                点号Entity
     * @param tmseg                点号时段类型
     * @param useMethod            使用的计算方法
     * @param beginTime            要计算的起始时间
     * @param endTime              要计算的截止时间
     * @param optype               操作类型，指示调用的来源：原始数据触发计算、点号数据触发计算、定时计算等；通过设置传入optype为UN_DEFINE控制只进行点号数据的计算
     * @param isManual             是否为人工操作
     * @param flowid               流程全局ID
     * @param fromNodeId           流程来源节点ID
     * @param regularTimeRawDatas  规则时间后的原始数据，时间和数值集合
     * @param tableCode            原始数据表编码
     * @param rawStcd              原始数据站点编码
     * @param rawColumnCode        原始数据列名
     * @param filterByStcdRawDatas 原始数据集合
     * @param connection           原始数据表所属的数据库连接信息
     * @return qc.module.qms.dto.calc.QmsPointDataCalcSaveResultDto
     * @author QuCheng Tech
     * @since 2025/10/6
     */
    QmsPointDataCalcSaveResultDto calc(QmsPoint point, QmsPointTimeSegmentEnum tmseg, QmsNumericCalcUseMethod useMethod,
                                       LocalDateTime beginTime, LocalDateTime endTime, QmsDataFlowOperateTypeEnum optype,
                                       boolean isManual, String flowid, String fromNodeId, Map<LocalDateTime, Double> regularTimeRawDatas,
                                       String tableCode, String rawStcd, String rawColumnCode, List<QmsRawDataDto> filterByStcdRawDatas,
                                       DBConnectionDto connection) {
        LocalDateTime now = LocalDateTimeUtil.getNow();

        //根据optype操作类型判断是哪种操作触发计算，区分是测试还是正式，测试时只返回结果不进行计算结果的保存操作
        //通过设置传入optype为UN_DEFINE控制只进行点号数据的计算，用于测试原始数据触发点号计算或者指定点号数据计算
        boolean isOnlyCalc = true;
        if (optype == QmsDataFlowOperateTypeEnum.CALC_POINT_DATA_BY_RAW_DATA || optype == QmsDataFlowOperateTypeEnum.CALC_POINT_DATA_BY_TIMER
                || optype == QmsDataFlowOperateTypeEnum.CALC_POINT_DATA_BY_MANUAL || optype == QmsDataFlowOperateTypeEnum.CALC_POINT_DATA_BY_DATA_TRIGGER)
            isOnlyCalc = false;

        //还需要传入计算触发的来源、是否要保存计算出的结果数据、如果是原始数据计算调用还需要传入原始数据
        QmsPointDataCalcSaveResultDto result = new QmsPointDataCalcSaveResultDto();

        String pointid = null;
        if (point != null)
            pointid = point.getPointid();

        //验证输入是否成功，不成功不进行计算
        boolean hasValidInputSuccess = true;
        String validInputMessage = "";
        //传入数据验证：点号对象、时段类型、起止时间
        if (point == null) {
            hasValidInputSuccess = false;
            validInputMessage += "要计算的点号对象不能为空" + "\r\n";
        } else if (StringUtils.isBlank(point.getPointid())) {
            hasValidInputSuccess = false;
            validInputMessage += "要计算的点号信息中点号编码不能为空" + "\r\n";
        }
        if (tmseg == QmsPointTimeSegmentEnum.UN_DEFINE) {
            hasValidInputSuccess = false;
            validInputMessage += "要计算的点号时段类型错误" + "\r\n";
        }
        if (beginTime == null) {
            hasValidInputSuccess = false;
            validInputMessage += "要计算的起始时间不能为空" + "\r\n";
        }
        if (endTime == null) {
            hasValidInputSuccess = false;
            validInputMessage += "要计算的截止时间不能为空" + "\r\n";
        }
        if (beginTime.isAfter(endTime)) {
            hasValidInputSuccess = false;
            validInputMessage += "要计算的起始时间不能大于截止时间" + "\r\n";
        }

        //如果optype是原始数据触发点号数据计算，输入的原始数据序列不能为空
        if (optype == QmsDataFlowOperateTypeEnum.CALC_POINT_DATA_BY_RAW_DATA) {
            if (regularTimeRawDatas == null || regularTimeRawDatas.size() < 0x1) {
                hasValidInputSuccess = false;
                validInputMessage += "原始数据计算时按时间规整后的数据集合不能为空" + "\r\n";
            }
            //判断原始数据输入信息：表编码、原始站点编号、列名、原始数据集合
            if (StringUtils.isBlank(tableCode)) {
                hasValidInputSuccess = false;
                validInputMessage += "原始数据计算时原始数据表编码不能为空" + "\r\n";
            }
            if (StringUtils.isBlank(rawStcd)) {
                hasValidInputSuccess = false;
                validInputMessage += "原始数据计算时原始数据站点编码不能为空" + "\r\n";
            }
            if (StringUtils.isBlank(rawColumnCode)) {
                hasValidInputSuccess = false;
                validInputMessage += "原始数据计算时原始数据表列名不能为空" + "\r\n";
            }
            if (filterByStcdRawDatas == null || filterByStcdRawDatas.size() < 0x1) {
                hasValidInputSuccess = false;
                validInputMessage += "原始数据计算时原始数据集合不能为空" + "\r\n";
            }
        }

        //传入计算方法验证
        if (useMethod == null) {
            hasValidInputSuccess = false;
            validInputMessage += "[" + optype + "]，点号[" + pointid + "]，时段类型[" + tmseg + "]，计算时间段[" + QmsTimeUtil.format(beginTime, tmseg)
                    + "]-[" + QmsTimeUtil.format(endTime, tmseg) + "]，无有效的计算方法，无法进行数值计算\r\n";

            if (isOnlyCalc == false) {//在同时进行计算和计算结果保存时进行流程日志消息的发送
                if (optype == QmsDataFlowOperateTypeEnum.CALC_POINT_DATA_BY_RAW_DATA) {
                    //无计算方法或无有效的计算方法
                    logger.warn("原始数据站点编码[" + rawStcd + "]，列名[" + rawColumnCode + "]，对应点号编码[" + pointid + "]，对应点号时段类型[" + tmseg + "]，无有效的计算方法");
                    //修改原始数据中的处理标记和处理时间
                    rawDataService.setFlag(tableCode, rawColumnCode, filterByStcdRawDatas, QmsRawDataCalcFlagEnum.NO_CALC_METHOD, connection);
                }
            }
        }

        //如果输入验证不通过，在此直接返回，不进行后续的计算处理
        if (hasValidInputSuccess == false) {
            logger.warn(validInputMessage);

            if (isOnlyCalc == false) {//在同时进行计算和计算结果保存时进行流程日志消息的发送
                //进行数据流程日志记录
                dataFlowLogProducer.send(null, flowid, fromNodeId, optype, isManual, now, null, false, validInputMessage,
                        pointid, tmseg, beginTime, endTime);
            }

            QmsPointDataCalcResultDto calcResult = new QmsPointDataCalcResultDto();
            calcResult.setSuccess(false);
            calcResult.setMsg(validInputMessage);

            result.setCalcResult(calcResult);

            return result;
        }

        //初始化计算结果对象，初始化为计算失败
        QmsPointDataCalcResultDto calcResult = new QmsPointDataCalcResultDto();
        calcResult.setSuccess(false);

        //计算目标对象
        QmsNumericCalcDest calcDest = new QmsNumericCalcDest();
        calcDest.setPoint(QmsPointMapper.MAPPER.toDto(point));
        calcDest.setTmseg(tmseg);
        //设置计算目标中的起止时间
        calcDest.setBeginTime(beginTime);
        calcDest.setEndTime(endTime);

        //计算输入,原始数据计算输入参数和数据获取：原始数据计算仅需要获取计算参数，不存在要输入其他点号时序数据序列（不进行判断获取处理）、也不存在要获取工作曲线作为计算输入的情况
        //原始数据计算是否存在不需要任何输入的情况（不需要计算输入数据，不需要计算输入工作曲线，也不需要计算输入参数--使用默认）
        QmsNumericCalcInput calcInput = numericCalcInputGetService.get(useMethod, calcDest);
        if (calcInput == null) {
            //获取到的输入为null表示错误，此时无法进行数值计算
            String errorMsg = "[" + optype + "]，点号[" + pointid + "]，时段类型[" + tmseg + "]，计算时间段[" + QmsTimeUtil.format(beginTime, tmseg)
                    + "]-[" + QmsTimeUtil.format(endTime, tmseg) + "]，获取到计算输入对象为null，无法进行数值计算";
            logger.warn(errorMsg);

            if (isOnlyCalc == false) {//在同时进行计算和计算结果保存时进行流程日志消息的发送
                if (optype == QmsDataFlowOperateTypeEnum.CALC_POINT_DATA_BY_RAW_DATA) {
                    //获取到的输入为null表示错误，此时无法进行数值计算
                    logger.warn("原始数据站点编码[" + rawStcd + "]，列名[" + rawColumnCode + "]，对应点号编码[" + pointid + "]，对应点号时段类型[" + tmseg + "]，获取到计算输入对象为null，无法进行数值计算");
                    //修改原始数据中的处理标记和处理时间，计算失败
                    rawDataService.setFlag(tableCode, rawColumnCode, filterByStcdRawDatas, QmsRawDataCalcFlagEnum.CALC_FAIL, connection);
                }

                //进行数据流程日志记录
                dataFlowLogProducer.send(null, flowid, fromNodeId, optype, isManual, now, null, false, errorMsg,
                        pointid, tmseg, beginTime, endTime);
            }

            //设置计算结果中的错误提示信息并返回
            calcResult.setMsg(errorMsg);

            result.setCalcResult(calcResult);
            return result;
        } else {
            //计算流程节点ID需要传入到点号数据保存流程中，因此需要生成一个节点ID进行传入；
            String calcNodeId = IdentifierUtil.simpleUUID();
            //在进行原始数据计算时根据站点查询检修和故障处理，判断数据质量；定时或人工进行点号数据计算时均设置数据质量为正常数据；
            List<QmsStationMaintenance> stationIsTestDataMaintenanceRecords = null;
            //调用计算方法
            //判断如果是原始数据计算，将传入的原始数据序列加入到计算输入数据序列的第1个序列位置
            if (optype == QmsDataFlowOperateTypeEnum.CALC_POINT_DATA_BY_RAW_DATA) {
                //在原始数据计算中查询站点的检修维护记录
                stationIsTestDataMaintenanceRecords = stationMaintenanceService.queryStationIsTestDataRecords(point.getStcd(), beginTime, endTime);
                //在原始数据计算中，需要将原始数据作为输入时序数据序列，将原始数据放入到输入数据的第0个序列中
                QmsNumericCalcInputTimeSerialDatas inputRawTimeSerialDatas = new QmsNumericCalcInputTimeSerialDatas();
                //原始数据序列信息中保持点号ID和站点编码为空
                inputRawTimeSerialDatas.setSerialName(tableCode + "-" + rawColumnCode);//序列名称使用：表编码-列名
                inputRawTimeSerialDatas.setTmseg(tmseg);
                //2025-9-19，实测发现输入的原始数据顺序颠倒，在此对输入的原始数据按时标升序排序
                if (regularTimeRawDatas.size() > 0x0) {
                    logger.debug("计算输入原始数据");
                    for (Map.Entry<LocalDateTime, Double> entry : regularTimeRawDatas.entrySet()) {
                        logger.debug(entry.getKey() + "\t" + entry.getValue());
                    }
                    //实测发现new的对象不能是HashMap(会自动根据key进行排序，导致数据是按时标Desc排列的，如果按序号取数据可能取的时间点不是预期时间点数据)
                    //修改为new的对象为LinkedHashMap后正常
                    Map<LocalDateTime, Double> sortedByTimeAscDatas = new LinkedHashMap<>();
                    //原始数据按时间Asc排序后放入计算方法的输入数据序列中
                    //inputRawTimeSerialDatas.setDatas(calcInputRawDatas.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
                    regularTimeRawDatas.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(p -> sortedByTimeAscDatas.put(p.getKey(), p.getValue()));

                    //调试测试输出使用，检查输入数据是否按时标Asc排序
                    logger.debug("计算输入原始数据按时间Asc排序后");
                    for (Map.Entry<LocalDateTime, Double> entry : sortedByTimeAscDatas.entrySet()) {
                        logger.debug(entry.getKey() + "\t" + entry.getValue());
                    }
                    inputRawTimeSerialDatas.setDatas(sortedByTimeAscDatas);
                }

                List<QmsNumericCalcInputTimeSerialDatas> timeSerielDatas = null;
                if (calcInput.getTimeSerialDatas() != null && calcInput.getTimeSerialDatas().size() > 0x0)
                    timeSerielDatas = calcInput.getTimeSerialDatas();
                else
                    timeSerielDatas = new ArrayList<>();
                //将原始数据序列放到输入时序数据的第0个中
                timeSerielDatas.add(0x0, inputRawTimeSerialDatas);
                calcInput.setTimeSerialDatas(timeSerielDatas);
            }

            try {
                QmsNumericCalcResult numericCalcResult = QmsNumericCalcUtil.calc(calcDest, useMethod, calcInput);
                //转换计算结果对象，因为QmsNumericCalcResult封装在algorithm不能直接返回给api层
                calcResult = QmsCalcResultMapper.MAPPER.toPointDataCalcDto(numericCalcResult);
                //设置数据计算结果
                result.setCalcResult(calcResult);

                if (calcResult != null && calcResult.isSuccess() == true && calcResult.getDatas() != null && calcResult.getDatas().size() > 0x0) {
                    String calcResultInfo = "[" + optype + "]，点号[" + pointid + "]，时段类型[" + tmseg + "]，计算时间段[" + QmsTimeUtil.format(beginTime, tmseg)
                            + "]-[" + QmsTimeUtil.format(endTime, tmseg) + "]，计算结果：是否成功[" + calcResult.isSuccess() + "]，提示信息[" + calcResult.getMsg()
                            + "]，应有数量[" + calcResult.getDueCount() + "]，实有数量[" + calcResult.getActualCount() + "]，计算出的点号数据条数[" + calcResult.getDatas().size() + "]";
                    logger.info(calcResultInfo);

                    //获取计算出数据中的点号数据的起止时间，进行计算流程日志记录
                    Map<LocalDateTime, Double> calcResultDatas = calcResult.getDatas();
                    List<LocalDateTime> calcResultDataTimes = calcResultDatas.keySet().stream().collect(Collectors.toList());
                    //LocalDateTime calcResultBeginTime = calcResultDataTimes.stream().min(Comparator.naturalOrder()).get();
                    LocalDateTime calcResultBeginTime = calcResultDataTimes.stream().min(LocalDateTime::compareTo).get();
                    LocalDateTime calcResultEndTime = calcResultDataTimes.stream().max(LocalDateTime::compareTo).get();

                    //调用点号数据管理中的保存方法
                    //计算方法得到的数据集合进行转换，如果没有时段初末和最值仅需要对tm和v进行赋值
                    List<QmsPointNumericSaveDataDto> calcPointDatas = new ArrayList<>();
                    for (Map.Entry<LocalDateTime, Double> entry : calcResultDatas.entrySet()) {
                        QmsPointNumericSaveDataDto numericData = new QmsPointNumericSaveDataDto();
                        numericData.setTm(entry.getKey());
                        numericData.setV(entry.getValue());
                        //设置计算数据质量
                        //在进行原始数据计算时根据站点查询检修和故障处理，判断数据质量；定时或人工进行点号数据计算时均设置数据质量为正常数据；
                        //初始化数据质量为有效--自动计算的数据质量均为正常数据
                        QmsPointDataQualityEnum quality = QmsPointDataQualityEnum.NORMAL;
                        if (optype == QmsDataFlowOperateTypeEnum.CALC_POINT_DATA_BY_RAW_DATA && stationIsTestDataMaintenanceRecords != null && stationIsTestDataMaintenanceRecords.size() > 0x0) {
                            //如果为计算原始数据，并且站点的检修维护记录不为空，判断检修时段是否包含当前时间，如果包含标记为测试数据
                            for (QmsStationMaintenance stationMaintenance : stationIsTestDataMaintenanceRecords) {
                                if (stationMaintenance.getBegintm().isAfter(entry.getKey()) == false) {
                                    //检修记录的工作开始时间≤数据时标
                                    if (stationMaintenance.getEndtm() == null || stationMaintenance.getEndtm().isBefore(entry.getKey()) == false) {
                                        //检修记录的工作结束时间为null、或者工作结束时间≥数据时标
                                        String setTestDataMsg = "[" + optype + "]，点号[" + pointid + "]，时段类型[" + tmseg + "]，数据时标[" + QmsTimeUtil.format(entry.getKey(), tmseg)
                                                + "]，数据值[" + entry.getValue() + "]，根据站点检修维护记录自动设置数据质量为测试数据，记录ID[" + stationMaintenance.getId() + "]，工作标题[" + stationMaintenance.getTitle()
                                                + "]，工作时间段[" + LocalDateTimeUtil.formatDateTime(stationMaintenance.getBegintm()) + "]-[" + LocalDateTimeUtil.formatDateTime(stationMaintenance.getEndtm()) + "]";
                                        logger.warn(setTestDataMsg);

                                        quality = QmsPointDataQualityEnum.TEST;
                                    }
                                }
                            }
                        }
                        numericData.setQuality(quality);

                        calcPointDatas.add(numericData);
                    }

                    if (isOnlyCalc == false) {//在同时进行计算和计算结果保存时进行流程日志消息的发送
                        if (optype == QmsDataFlowOperateTypeEnum.CALC_POINT_DATA_BY_RAW_DATA) {
                            //修改原始数据标记；传入完整的原始数据信息，如果原始数据未保存到数据库中进行一次保存（同时设置处理标记）
                            rawDataService.setFlag(tableCode, rawColumnCode, filterByStcdRawDatas, QmsRawDataCalcFlagEnum.SUCCESS, connection);
                        }

                        //进行数据流程日志记录
                        dataFlowLogProducer.send(calcNodeId, flowid, fromNodeId, optype, isManual, now, null, true, calcResultInfo,
                                pointid, tmseg, calcResultBeginTime, calcResultEndTime);

                        //保存计算出的当前点号数据，无需获取返回结果，在保存点号数据方法中判断处理本次实际保存的数据，并将变化的点号数据集合放入消息队列中进行后续的关联点号计算、数据预警和转发处理
                        QmsPointDataManageResultDto savePointDataResult = pointDataManageService.saveSystemCalcNumericDatas(pointid, tmseg, calcPointDatas, optype, flowid, calcNodeId);

                        //设置数据保存结果
                        result.setSaveResult(savePointDataResult);

                        String saveResultInfo = "[" + optype + "]，点号[" + pointid + "]，时段类型[" + tmseg + "]，保存数据时间段[" + QmsTimeUtil.format(calcResultBeginTime, tmseg)
                                + "]-[" + QmsTimeUtil.format(calcResultEndTime, tmseg) + "]，保存点号数据结果：是否成功[" + savePointDataResult.isSuccess()
                                + "]，受影响条数[" + savePointDataResult.getAffectCount() + "]，保存结果信息[" + savePointDataResult.getMsg() + "]";
                        logger.info(saveResultInfo);
                    }
                } else {
                    String calcFailMsg = "调用计算方法计算失败或计算出的结果为空";
                    if (calcResult == null)
                        calcFailMsg = "调用计算方法结果对象为空";
                    else {
                        if (calcResult.isSuccess() == false) {
                            calcFailMsg = "调用计算方法计算失败，错误信息为：" + calcResult.getMsg();
                        } else if (calcResult.getDatas() == null || calcResult.getDatas().size() < 0x1) {
                            calcFailMsg = "调用计算方法计算成功，得到的数值集合为空";
                        }
                    }
                    //计算结果为空、计算失败、或者计算结果中的数值集合为空
                    String errorMsg = "[" + optype + "]，点号[" + pointid + "]，时段类型[" + tmseg + "]，计算时间段[" + QmsTimeUtil.format(beginTime, tmseg)
                            + "]-[" + QmsTimeUtil.format(endTime, tmseg) + "]，" + calcFailMsg;
                    logger.warn(errorMsg);

                    if (isOnlyCalc == false) {//在同时进行计算和计算结果保存时进行流程日志消息的发送
                        if (optype == QmsDataFlowOperateTypeEnum.CALC_POINT_DATA_BY_RAW_DATA) {
                            //计算结果为空、计算失败、或者计算结果中的数值集合为空
                            logger.warn("原始数据站点编码[" + rawStcd + "]，列名[" + rawColumnCode + "]，对应点号编码[" + pointid + "]，对应点号时段类型[" + tmseg + "]，" + calcFailMsg);
                            //修改原始数据中的处理标记和处理时间，计算失败
                            rawDataService.setFlag(tableCode, rawColumnCode, filterByStcdRawDatas, QmsRawDataCalcFlagEnum.CALC_FAIL, connection);
                        }

                        //进行数据流程日志记录
                        dataFlowLogProducer.send(null, flowid, fromNodeId, optype, isManual, now, null, false, errorMsg,
                                pointid, tmseg, beginTime, endTime);
                    }
                }
            } catch (Exception ex) {
                //调用计算模块时异常，可能时输入数据或参数错误，或者时计算模块内部错误
                //获取异常信息进行数据流程日志记录
                //计算结果为空、计算失败、或者计算结果中的数值集合为空
                String errorMsg = "[" + optype + "]，点号[" + pointid + "]，时段类型[" + tmseg + "]，计算时间段[" + QmsTimeUtil.format(beginTime, tmseg)
                        + "]-[" + QmsTimeUtil.format(endTime, tmseg) + "]，计算时发生异常，" + ex.getMessage();
                logger.error(errorMsg);

                if (isOnlyCalc == false) {//在同时进行计算和计算结果保存时进行流程日志消息的发送
                    if (optype == QmsDataFlowOperateTypeEnum.CALC_POINT_DATA_BY_RAW_DATA) {
                        //获取异常信息进行数据流程日志记录，此时标记原始数据处理失败
                        logger.error("原始数据站点编码[" + rawStcd + "]，列名[" + rawColumnCode + "]，对应点号编码[" + pointid + "]，对应点号时段类型[" + tmseg + "]，" + ex.getMessage());
                        //修改原始数据中的处理标记和处理时间，计算失败
                        rawDataService.setFlag(tableCode, rawColumnCode, filterByStcdRawDatas, QmsRawDataCalcFlagEnum.CALC_FAIL, connection);
                    }

                    //进行数据流程日志记录
                    dataFlowLogProducer.send(null, flowid, fromNodeId, optype, isManual, now, null, false, errorMsg,
                            pointid, tmseg, beginTime, endTime);
                }
            }
        }


        return result;
    }
}
